home *** CD-ROM | disk | FTP | other *** search
/ Supercompiler 1997 / SUPERCOMPILER97.iso / Delphi 3.0 / DATA.Z / mask.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  1997-01-29  |  40.6 KB  |  1,522 lines

  1.  
  2. {*******************************************************}
  3. {                                                       }
  4. {       Delphi Visual Component Library                 }
  5. {                                                       }
  6. {       Copyright (c) 1995,97 Borland International     }
  7. {                                                       }
  8. {*******************************************************}
  9.  
  10. unit Mask;
  11.  
  12. {$R-}
  13.  
  14. interface
  15.  
  16. uses Windows, SysUtils, Classes, StdCtrls, Controls, Messages,
  17.   Forms, Graphics, Menus;
  18.  
  19. const
  20.   DefaultBlank: Char = '_';
  21.   MaskFieldSeparator: Char = ';';
  22.   MaskNoSave: Char = '0';
  23.  
  24.   mDirReverse = '!';         { removes leading blanks if true, else trailing blanks}
  25.   mDirUpperCase = '>';       { all chars that follow to upper case }
  26.   mDirLowerCase = '<';       { all chars that follow to lower case }
  27.                              { '<>' means remove casing directive }
  28.   mDirLiteral = '\';         { char that immediately follows is a literal }
  29.  
  30.   mMskAlpha = 'L';           { in US = A-Z,a-z }
  31.   mMskAlphaOpt = 'l';
  32.   mMskAlphaNum = 'A';        { in US = A-Z,a-z,0-9 }
  33.   mMskAlphaNumOpt  = 'a';
  34.   mMskAscii = 'C';           { any character}
  35.   mMskAsciiOpt = 'c';
  36.   mMskNumeric = '0';         { 0-9, no plus or minus }
  37.   mMskNumericOpt = '9';
  38.   mMskNumSymOpt = '#';       { 0-9, plus and minus }
  39.  
  40.    { intl literals }
  41.   mMskTimeSeparator = ':';
  42.   mMskDateSeparator = '/';
  43.  
  44. type
  45.  
  46.   TMaskCharType = (mcNone, mcLiteral, mcIntlLiteral, mcDirective, mcMask,
  47.     mcMaskOpt, mcFieldSeparator, mcField);
  48.   TMaskDirectives = set of (mdReverseDir, mdUpperCase, mdLowerCase,
  49.     mdLiteralChar);
  50.  
  51. type
  52. { Exception class }
  53.   EDBEditError = class(Exception);
  54.  
  55.   TMaskedState = set of (msMasked, msReEnter, msDBSetText);
  56.  
  57. { TCustomMaskEdit }
  58.  
  59.   TCustomMaskEdit = class(TCustomEdit)
  60.   private
  61.     FEditMask: string;
  62.     FMaskBlank: Char;
  63.     FMaxChars: Integer;
  64.     FMaskSave: Boolean;
  65.     FMaskState: TMaskedState;
  66.     FCaretPos: Integer;
  67.     FBtnDownX: Integer;
  68.     FOldValue: string;
  69.     function DoInputChar(var NewChar: Char; MaskOffset: Integer): Boolean;
  70.     function Validate(const Value: string; var Pos: Integer): Boolean;
  71.     function InputChar(var NewChar: Char; Offset: Integer): Boolean;
  72.     function DeleteSelection(var Value: string; Offset: Integer;
  73.       Len: Integer): Boolean;
  74.     function InputString(var Value: string; const NewValue: string;
  75.       Offset: Integer): Integer;
  76.     function AddEditFormat(const Value: string; Active: Boolean): string;
  77.     function RemoveEditFormat(const Value: string): string;
  78.     function FindLiteralChar (MaskOffset: Integer; InChar: Char): Integer;
  79.     function GetEditText: string;
  80.     function GetMasked: Boolean;
  81.     function GetText: string;
  82.     function GetMaxLength: Integer;
  83.     function CharKeys(var CharCode: Char): Boolean;
  84.     procedure SetEditText(const Value: string);
  85.     procedure SetEditMask(const Value: string);
  86.     procedure SetMaxLength(Value: Integer);
  87.     procedure SetText(const Value: string);
  88.     procedure DeleteKeys(CharCode: Word);
  89.     procedure HomeEndKeys(CharCode: Word; Shift: TShiftState);
  90.     procedure CursorInc(CursorPos: Integer; Incr: Integer);
  91.     procedure CursorDec(CursorPos: Integer);
  92.     procedure ArrowKeys(CharCode: Word; Shift: TShiftState);
  93.     procedure WMLButtonDown(var Message: TWMLButtonDown); message WM_LBUTTONDOWN;
  94.     procedure WMLButtonUp(var Message: TWMLButtonUp); message WM_LBUTTONUP;
  95.     procedure WMSetFocus(var Message: TWMSetFocus); message WM_SETFOCUS;
  96.     procedure WMCut(var Message: TMessage); message WM_CUT;
  97.     procedure WMPaste(var Message: TMessage); message WM_PASTE;
  98.     procedure CMEnter(var Message: TCMEnter); message CM_ENTER;
  99.     procedure CMExit(var Message: TCMExit);   message CM_EXIT;
  100.     procedure CMTextChanged(var Message: TMessage); message CM_TEXTCHANGED;
  101.     procedure CMWantSpecialKey(var Message: TCMWantSpecialKey); message CM_WANTSPECIALKEY;
  102.   protected
  103.     procedure ReformatText(const NewMask: string);
  104.     procedure GetSel(var SelStart: Integer; var SelStop: Integer);
  105.     procedure SetSel(SelStart: Integer; SelStop: Integer);
  106.     procedure SetCursor(Pos: Integer);
  107.     procedure KeyDown(var Key: Word; Shift: TShiftState); override;
  108.     procedure KeyUp(var Key: Word; Shift: TShiftState); override;
  109.     procedure KeyPress(var Key: Char); override;
  110.     function EditCanModify: Boolean; virtual;
  111.     procedure Reset; virtual;
  112.     function GetFirstEditChar: Integer;
  113.     function GetLastEditChar: Integer;
  114.     function GetNextEditChar(Offset: Integer): Integer;
  115.     function GetPriorEditChar(Offset: Integer): Integer;
  116.     function GetMaxChars: Integer;
  117.     procedure ValidateError;
  118.     procedure CheckCursor;
  119.     property EditMask: string read FEditMask write SetEditMask;
  120.     property MaskState: TMaskedState read FMaskState write FMaskState;
  121.     property MaxLength: Integer read GetMaxLength write SetMaxLength;
  122.   public
  123.     constructor Create(AOwner: TComponent); override;
  124.     procedure ValidateEdit;
  125.     procedure Clear;
  126.     function GetTextLen: Integer;
  127.     property IsMasked: Boolean read GetMasked;
  128.     property EditText: string read GetEditText write SetEditText;
  129.     property Text: string read GetText write SetText;
  130.   end;
  131.  
  132. { TMaskEdit }
  133.  
  134.   TMaskEdit = class(TCustomMaskEdit)
  135.   published
  136.     property AutoSelect;
  137.     property AutoSize;
  138.     property BorderStyle;
  139.     property CharCase;
  140.     property Color;
  141.     property Ctl3D;
  142.     property DragCursor;
  143.     property DragMode;
  144.     property Enabled;
  145.     property EditMask;
  146.     property Font;
  147.     property ImeMode;
  148.     property ImeName;
  149.     property MaxLength;
  150.     property ParentColor;
  151.     property ParentCtl3D;
  152.     property ParentFont;
  153.     property ParentShowHint;
  154.     property PasswordChar;
  155.     property PopupMenu;
  156.     property ReadOnly;
  157.     property ShowHint;
  158.     property TabOrder;
  159.     property TabStop;
  160.     property Text;
  161.     property Visible;
  162.     property OnChange;
  163.     property OnClick;
  164.     property OnDblClick;
  165.     property OnDragDrop;
  166.     property OnDragOver;
  167.     property OnEndDrag;
  168.     property OnEnter;
  169.     property OnExit;
  170.     property OnKeyDown;
  171.     property OnKeyPress;
  172.     property OnKeyUp;
  173.     property OnMouseDown;
  174.     property OnMouseMove;
  175.     property OnMouseUp;
  176.     property OnStartDrag;
  177.   end;
  178.  
  179. function FormatMaskText(const EditMask: string; const Value: string): string;
  180. function MaskGetMaskSave(const EditMask: string): Boolean;
  181. function MaskGetMaskBlank(const EditMask: string): Char;
  182. function MaskGetFldSeparator(const EditMask: string): Integer;
  183.  
  184.  
  185. implementation
  186.  
  187. uses Clipbrd, Consts;
  188.  
  189. { Mask utility routines }
  190.  
  191. function MaskGetCharType(const EditMask: string; MaskOffset: Integer): TMaskCharType;
  192. var
  193.   MaskChar: Char;
  194. begin
  195.   Result := mcLiteral;
  196.   MaskChar := #0;
  197.   if MaskOffset <= Length(EditMask) then
  198.     MaskChar := EditMask[MaskOffset];
  199.   if MaskOffset > Length(EditMask) then
  200.     Result := mcNone
  201.  
  202.   else if (MaskOffset > 1) and (EditMask[MaskOffset - 1] = mDirLiteral) and
  203.       not ((MaskOffset > 2) and (EditMask[MaskOffset - 2] = mDirLiteral)) then
  204.     Result := mcLiteral
  205.  
  206.   else if (MaskChar = MaskFieldSeparator) and
  207.          (Length(EditMask) >= 4) and
  208.          (MaskOffset > Length(EditMask) - 4) then
  209.     Result := mcFieldSeparator
  210.  
  211.   else if (Length(EditMask) >= 4) and
  212.          (MaskOffset > (Length(EditMask) - 4)) and
  213.          (EditMask[MaskOffset - 1] = MaskFieldSeparator) and
  214.           not ((MaskOffset > 2) and
  215.                     (EditMask[MaskOffset - 2] = mDirLiteral)) then
  216.     Result := mcField
  217.  
  218.   else if MaskChar in [mMskTimeSeparator, mMskDateSeparator] then
  219.     Result := mcIntlLiteral
  220.  
  221.   else if MaskChar in [mDirReverse, mDirUpperCase, mDirLowerCase,
  222.       mDirLiteral] then
  223.     Result := mcDirective
  224.  
  225.   else if MaskChar in [mMskAlphaOpt, mMskAlphaNumOpt, mMskAsciiOpt,
  226.       mMskNumSymOpt, mMskNumericOpt] then
  227.     Result := mcMaskOpt
  228.  
  229.   else if MaskChar in [mMskAlpha, mMskAlphaNum, mMskAscii, mMskNumeric] then
  230.     Result := mcMask;
  231. end;
  232.  
  233. function MaskGetCurrentDirectives(const EditMask: string;
  234.   MaskOffset: Integer): TMaskDirectives;
  235. var
  236.   I: Integer;
  237.   MaskChar: Char;
  238. begin
  239.   Result := [];
  240.   for I := 1 to Length(EditMask) do
  241.   begin
  242.     MaskChar := EditMask[I];
  243.     if (MaskChar = mDirReverse) then
  244.       Include(Result, mdReverseDir)
  245.     else if (MaskChar = mDirUpperCase) and (I < MaskOffset) then
  246.     begin
  247.       Exclude(Result, mdLowerCase);
  248.       if not ((I > 1) and (EditMask[I-1] = mDirLowerCase)) then
  249.         Include(Result, mdUpperCase);
  250.     end
  251.     else if (MaskChar = mDirLowerCase) and (I < MaskOffset) then
  252.     begin
  253.       Exclude(Result, mdUpperCase);
  254.       Include(Result, mdLowerCase);
  255.     end;
  256.   end;
  257.   if MaskGetCharType(EditMask, MaskOffset) = mcLiteral then
  258.     Include(Result, mdLiteralChar);
  259. end;
  260.  
  261. function MaskIntlLiteralToChar(IChar: Char): Char;
  262. begin
  263.   Result := IChar;
  264.   case IChar of
  265.     mMskTimeSeparator: Result := TimeSeparator;
  266.     mMskDateSeparator: Result := DateSeparator;
  267.   end;
  268. end;
  269.  
  270. function MaskDoFormatText(const EditMask: string; const Value: string;
  271.   Blank: Char): string;
  272. var
  273.   I: Integer;
  274.   Offset, MaskOffset: Integer;
  275.   CType: TMaskCharType;
  276.   Dir: TMaskDirectives;
  277. begin
  278.   Result := Value;
  279.   Dir := MaskGetCurrentDirectives(EditMask, 1);
  280.  
  281.   if not (mdReverseDir in Dir) then
  282.   begin
  283.       { starting at the beginning, insert literal chars in the string
  284.         and add spaces on the end }
  285.     Offset := 1;
  286.     for MaskOffset := 1 to Length(EditMask) do
  287.     begin
  288.       CType := MaskGetCharType(EditMask, MaskOffset);
  289.  
  290.       if CType in [mcLiteral, mcIntlLiteral] then
  291.       begin
  292.         Result := Copy(Result, 1, Offset - 1) +
  293.           MaskIntlLiteralToChar(EditMask[MaskOffset]) +
  294.           Copy(Result, Offset, Length(Result) - Offset + 1);
  295.         Inc(Offset);
  296.       end
  297.       else if CType in [mcMask, mcMaskOpt] then
  298.       begin
  299.         if Offset > Length(Result) then
  300.           Result := Result + Blank;
  301.         Inc(Offset);
  302.       end;
  303.     end;
  304.   end
  305.   else
  306.   begin
  307.       { starting at the end, insert literal chars in the string
  308.         and add spaces at the beginning }
  309.     Offset := Length(Result);
  310.     for I := 0 to(Length(EditMask) - 1) do
  311.     begin
  312.       MaskOffset := Length(EditMask) - I;
  313.       CType := MaskGetCharType(EditMask, MaskOffset);
  314.       if CType in [mcLiteral, mcIntlLiteral] then
  315.       begin
  316.         Result := Copy(Result, 1, Offset) +
  317.                MaskIntlLiteralToChar(EditMask[MaskOffset]) +
  318.                Copy(Result, Offset + 1, Length(Result) - Offset);
  319.       end
  320.       else if CType in [mcMask, mcMaskOpt] then
  321.       begin
  322.         if Offset < 1 then
  323.           Result := Blank + Result
  324.         else
  325.           Dec(Offset);
  326.       end;
  327.     end;
  328.   end;
  329. end;
  330.  
  331. function MaskGetMaskSave(const EditMask: string): Boolean;
  332. var
  333.   I: Integer;
  334.   Sep1, Sep2: Integer;
  335. begin
  336.   Result := True;
  337.   if Length(EditMask) >= 4 then
  338.   begin
  339.     Sep1 := -1;
  340.     Sep2 := -1;
  341.     I := Length(EditMask);
  342.     while Sep2 < 0 do
  343.     begin
  344.       if (MaskGetCharType(EditMask, I) =  mcFieldSeparator) then
  345.       begin
  346.         if Sep1 < 0 then
  347.           Sep1 := I
  348.         else
  349.           Sep2 := I;
  350.       end;
  351.       Dec(I);
  352.       if (I <= 0) or(I < Length(EditMask) - 4) then
  353.         Break;
  354.     end;
  355.     if Sep2 < 0 then
  356.       Sep2 := Sep1;
  357.     if Sep2 <> Length(EditMask) then
  358.       Result := not (EditMask [Sep2 + 1] = MaskNoSave);
  359.   end;
  360. end;
  361.  
  362. function MaskGetMaskBlank(const EditMask: string): Char;
  363. begin
  364.   Result := DefaultBlank;
  365.   if Length(EditMask) >= 4 then
  366.   begin
  367.     if (MaskGetCharType(EditMask, Length(EditMask) - 1) =
  368.                                                   mcFieldSeparator) then
  369.     begin
  370.         {in order for blank specifier to be valid, there
  371.          must also be a save specifier }
  372.       if (MaskGetCharType(EditMask, Length(EditMask) - 2) =
  373.                                                   mcFieldSeparator) or
  374.         (MaskGetCharType(EditMask, Length(EditMask) - 3) =
  375.                                                   mcFieldSeparator) then
  376.       begin
  377.         Result := EditMask [Length(EditMask)];
  378.       end;
  379.     end;
  380.   end;
  381. end;
  382.  
  383. function MaskGetFldSeparator(const EditMask: String): Integer;
  384. var
  385.   I: Integer;
  386. begin
  387.   Result := -1;
  388.   if Length(EditMask) >= 4 then
  389.   begin
  390.     for I := (Length(EditMask) - 4) to Length(EditMask) do
  391.     begin
  392.       if (MaskGetCharType(EditMask, I) = mcFieldSeparator) then
  393.       begin
  394.         Result := I;
  395.         Exit;
  396.       end;
  397.     end;
  398.   end;
  399. end;
  400.  
  401. function MaskOffsetToOffset(const EditMask: String; MaskOffset: Integer): Integer;
  402. var
  403.   I: Integer;
  404.   CType: TMaskCharType;
  405. begin
  406.   Result := 0;
  407.   for I := 1 to MaskOffset do
  408.   begin
  409.     CType := MaskGetCharType(EditMask, I);
  410.     if not (CType in [mcDirective, mcField, mcFieldSeparator]) then
  411.       Inc(Result);
  412.   end;
  413. end;
  414.  
  415. function OffsetToMaskOffset(const EditMask: string; Offset: Integer): Integer;
  416. var
  417.   I: Integer;
  418.   Count: Integer;
  419.   MaxChars: Integer;
  420. begin
  421.   MaxChars  := MaskOffsetToOffset(EditMask, Length(EditMask));
  422.   if Offset > MaxChars then
  423.   begin
  424.     Result := -1;
  425.     Exit;
  426.   end;
  427.  
  428.   Result := 0;
  429.   Count := Offset;
  430.   for I := 1 to Length(EditMask) do
  431.   begin
  432.     Inc(Result);
  433.     if not (mcDirective = MaskGetCharType(EditMask, I)) then
  434.     begin
  435.       Dec(Count);
  436.       if Count < 0 then
  437.         Exit;
  438.     end;
  439.   end;
  440. end;
  441.  
  442. function IsLiteralChar(const EditMask: string; Offset: Integer): Boolean;
  443. var
  444.   MaskOffset: Integer;
  445.   CType: TMaskCharType;
  446. begin
  447.   Result := False;
  448.   MaskOffset := OffsetToMaskOffset(EditMask, Offset);
  449.   if MaskOffset >= 0 then
  450.   begin
  451.     CType := MaskGetCharType(EditMask, MaskOffset);
  452.     Result := CType in [mcLiteral, mcIntlLiteral];
  453.   end;
  454. end;
  455.  
  456. function PadSubField(const EditMask: String; const Value: string;
  457.   StartFld, StopFld, Len: Integer; Blank: Char): string;
  458. var
  459.   Dir: TMaskDirectives;
  460.   StartPad: Integer;
  461.   K: Integer;
  462. begin
  463.   if (StopFld - StartFld) < Len then
  464.   begin
  465.      { found literal at position J, now pad it }
  466.     Dir := MaskGetCurrentDirectives(EditMask, 1);
  467.     StartPad := StopFld - 1;
  468.     if mdReverseDir in Dir then
  469.       StartPad := StartFld - 1;
  470.     Result := Copy(Value, 1, StartPad);
  471.     for K := 1 to (Len - (StopFld - StartFld)) do
  472.       Result := Result + Blank;
  473.     Result := Result + Copy(Value, StartPad + 1, Length(Value));
  474.   end
  475.   else if (StopFld - StartFld) > Len then
  476.   begin
  477.     Dir := MaskGetCurrentDirectives(EditMask, 1);
  478.     if mdReverseDir in Dir then
  479.       Result := Copy(Value, 1, StartFld - 1) +
  480.         Copy(Value, StopFld - Len, Length(Value))
  481.     else
  482.       Result := Copy(Value, 1, StartFld + Len - 1) +
  483.         Copy(Value, StopFld, Length(Value));
  484.   end
  485.   else
  486.     Result := Value;
  487. end;
  488.  
  489. function PadInputLiterals(const EditMask: String; const Value: string;
  490.   Blank: Char): string;
  491. var
  492.   J: Integer;
  493.   LastLiteral, EndSubFld: Integer;
  494.   Offset, MaskOffset: Integer;
  495.   CType: TMaskCharType;
  496.   MaxChars: Integer;
  497. begin
  498.   LastLiteral := 0;
  499.  
  500.   Result := Value;
  501.   for MaskOffset := 1 to Length(EditMask) do
  502.   begin
  503.     CType := MaskGetCharType(EditMask, MaskOffset);
  504.     if CType in [mcLiteral, mcIntlLiteral] then
  505.     begin
  506.       Offset := MaskOffsetToOffset(EditMask, MaskOffset);
  507.       EndSubFld := Length(Result) + 1;
  508.       for J := LastLiteral + 1 to Length(Result) do
  509.       begin
  510.         if Result[J] = MaskIntlLiteralToChar(EditMask[MaskOffset]) then
  511.         begin
  512.           EndSubFld := J;
  513.           Break;
  514.         end;
  515.       end;
  516.        { we have found a subfield, ensure that it complies }
  517.       if EndSubFld > Length(Result) then
  518.         Result := Result + MaskIntlLiteralToChar(EditMask[MaskOffset]);
  519.       Result := PadSubField(EditMask, Result, LastLiteral + 1, EndSubFld,
  520.         Offset - (LastLiteral + 1), Blank);
  521.       LastLiteral := Offset;
  522.     end;
  523.   end;
  524.  
  525.     {ensure that the remainder complies, too }
  526.   MaxChars  := MaskOffsetToOffset(EditMask, Length(EditMask));
  527.   if Length (Result) <> MaxChars then
  528.     Result := PadSubField(EditMask, Result, LastLiteral + 1, Length (Result) + 1,
  529.       MaxChars - LastLiteral, Blank);
  530.  
  531.     { replace non-literal blanks with blank char }
  532.   for Offset := 1 to Length (Result) do
  533.   begin
  534.     if Result[Offset] = ' ' then
  535.     begin
  536.       if not IsLiteralChar(EditMask, Offset - 1) then
  537.         Result[Offset] := Blank;
  538.     end;
  539.   end;
  540. end;
  541.  
  542. function FormatMaskText(const EditMask: string; const Value: string ): string;
  543. begin
  544.   if MaskGetMaskSave(EditMask) then
  545.     Result := PadInputLiterals(EditMask, Value, ' ')
  546.   else
  547.     Result := MaskDoFormatText(EditMask, Value, ' ');
  548. end;
  549.  
  550.  
  551. { TCustomMaskEdit }
  552.  
  553. constructor TCustomMaskEdit.Create(AOwner: TComponent);
  554. begin
  555.   inherited Create(AOwner);
  556.   FMaskState := [];
  557.   FMaskBlank := DefaultBlank;
  558. end;
  559.  
  560. procedure TCustomMaskEdit.KeyDown(var Key: Word; Shift: TShiftState);
  561. begin
  562.   inherited KeyDown(Key, Shift);
  563.   if IsMasked and (Key <> 0) and not (ssAlt in Shift) then
  564.   begin
  565.     if (Key = VK_LEFT) or(Key = VK_RIGHT) then
  566.     begin
  567.       ArrowKeys(Key, Shift);
  568.       if not ((ssShift in Shift) or (ssCtrl in Shift)) then
  569.         Key := 0;
  570.       Exit;
  571.     end
  572.     else if (Key = VK_UP) or(Key = VK_DOWN) then
  573.     begin
  574.       Key := 0;
  575.       Exit;
  576.     end
  577.     else if (Key = VK_HOME) or(Key = VK_END) then
  578.     begin
  579.       HomeEndKeys(Key, Shift);
  580.       Key := 0;
  581.       Exit;
  582.     end
  583.     else if ((Key = VK_DELETE) and ([ssShift, ssCtrl] * Shift = [])) or
  584.       (Key = VK_BACK) then
  585.     begin
  586.       if EditCanModify then
  587.         DeleteKeys(Key);
  588.       Key := 0;
  589.       Exit;
  590.     end;
  591.     CheckCursor;
  592.   end;
  593. end;
  594.  
  595. procedure TCustomMaskEdit.KeyUp(var Key: Word; Shift: TShiftState);
  596. begin
  597.   inherited KeyUp(Key, Shift);
  598.   if IsMasked and (Key <> 0) then
  599.   begin
  600.     if ((Key = VK_LEFT) or(Key = VK_RIGHT)) and (ssCtrl in Shift) then
  601.       CheckCursor;
  602.   end;
  603. end;
  604.  
  605. procedure TCustomMaskEdit.KeyPress(var Key: Char);
  606. begin
  607.   inherited KeyPress(Key);
  608.   if IsMasked and (Key <> #0) and not (Char(Key) in [^V, ^X, ^C]) then
  609.   begin
  610.     CharKeys(Key);
  611.     Key := #0;
  612.   end;
  613. end;
  614.  
  615. procedure TCustomMaskEdit.WMLButtonDown(var Message: TWMLButtonDown);
  616. begin
  617.   inherited;
  618.   FBtnDownX := Message.XPos;
  619. end;
  620.  
  621. procedure TCustomMaskEdit.WMLButtonUp(var Message: TWMLButtonUp);
  622. var
  623.   SelStart, SelStop : Integer;
  624. begin
  625.   inherited;
  626.   if (IsMasked) then
  627.   begin
  628.     GetSel(SelStart, SelStop);
  629.     FCaretPos := SelStart;
  630.     if (SelStart <> SelStop) and (Message.XPos > FBtnDownX) then
  631.       FCaretPos := SelStop;
  632.     CheckCursor;
  633.   end;
  634. end;
  635.  
  636. procedure TCustomMaskEdit.WMSetFocus(var Message: TWMSetFocus);
  637. begin
  638.   inherited;
  639.   if (IsMasked) then
  640.     CheckCursor;
  641. end;
  642.  
  643. procedure TCustomMaskEdit.SetEditText(const Value: string);
  644. begin
  645.   if GetEditText <> Value then
  646.   begin
  647.     SetTextBuf(PChar(Value));
  648.     CheckCursor;
  649.   end;
  650. end;
  651.  
  652. function TCustomMaskEdit.GetEditText: string;
  653. begin
  654.   Result := inherited Text;
  655. end;
  656.  
  657. function TCustomMaskEdit.GetTextLen: Integer;
  658. begin
  659.   Result := Length(Text);
  660. end;
  661.  
  662. function TCustomMaskEdit.GetText: string;
  663. begin
  664.   if not IsMasked then
  665.     Result := inherited Text
  666.   else
  667.   begin
  668.     Result := RemoveEditFormat(EditText);
  669.     if FMaskSave then
  670.       Result := AddEditFormat(Result, False);
  671.   end;
  672. end;
  673.  
  674. procedure TCustomMaskEdit.SetText(const Value: string);
  675. var
  676.   OldText: string;
  677.   Pos: Integer;
  678. begin
  679.   if not IsMasked then
  680.     inherited Text := Value
  681.   else
  682.   begin
  683.     OldText := Value;
  684.     if FMaskSave then
  685.       OldText := PadInputLiterals(EditMask, OldText, FMaskBlank)
  686.     else
  687.       OldText := AddEditFormat(OldText, True);
  688.     if not (msDBSetText in FMaskState) and
  689.       (csDesigning in ComponentState) and
  690.       not (csLoading in ComponentState) and
  691.       not Validate(OldText, Pos) then
  692.       raise EDBEditError.Create(SMaskErr);
  693.     EditText := OldText;
  694.   end;
  695. end;
  696.  
  697. procedure TCustomMaskEdit.WMCut(var Message: TMessage);
  698. begin
  699.   if not (IsMasked) then
  700.     inherited
  701.   else
  702.   begin
  703.     CopyToClipboard;
  704.     DeleteKeys(VK_DELETE);
  705.   end;
  706. end;
  707.  
  708. procedure TCustomMaskEdit.WMPaste(var Message: TMessage);
  709. var
  710.   Value: string;
  711.   Str: string;
  712.   SelStart, SelStop : Integer;
  713. begin
  714.   if not (IsMasked) or ReadOnly then
  715.     inherited
  716.   else
  717.   begin
  718.     Clipboard.Open;
  719.     Value := Clipboard.AsText;
  720.     Clipboard.Close;
  721.  
  722.     GetSel(SelStart, SelStop);
  723.     Str := EditText;
  724.     DeleteSelection(Str, SelStart, SelStop - SelStart);
  725.     EditText := Str;
  726.     SelStart := InputString(Str, Value, SelStart);
  727.     EditText := Str;
  728.     SetCursor(SelStart);
  729.   end;
  730. end;
  731.  
  732. function TCustomMaskEdit.GetMasked: Boolean;
  733. begin
  734.   Result := EditMask <> '';
  735. end;
  736.  
  737. function TCustomMaskEdit.GetMaxChars: Integer;
  738. begin
  739.   if IsMasked then
  740.     Result := FMaxChars
  741.   else
  742.     Result := inherited GetTextLen;
  743. end;
  744.  
  745. procedure TCustomMaskEdit.ReformatText(const NewMask: string);
  746. var
  747.   OldText: string;
  748. begin
  749.   OldText := RemoveEditFormat(EditText);
  750.   FEditMask := NewMask;
  751.   FMaxChars  := MaskOffsetToOffset(EditMask, Length(NewMask));
  752.   FMaskSave  := MaskGetMaskSave(NewMask);
  753.   FMaskBlank := MaskGetMaskBlank(NewMask);
  754.   OldText := AddEditFormat(OldText, True);
  755.   EditText := OldText;
  756. end;
  757.  
  758. procedure TCustomMaskEdit.SetEditMask(const Value: string);
  759. var
  760.   SelStart, SelStop: Integer;
  761. begin
  762.   if Value <> EditMask then
  763.   begin
  764.     if (csDesigning in ComponentState) and (Value <> '') and
  765.       not (csLoading in ComponentState) then
  766.       EditText := '';
  767.     if HandleAllocated then GetSel(SelStart, SelStop);
  768.     ReformatText(Value);
  769.     Exclude(FMaskState, msMasked);
  770.     if EditMask <> '' then Include(FMaskState, msMasked);
  771.     inherited MaxLength := 0;
  772.     if IsMasked and (FMaxChars > 0) then
  773.       inherited MaxLength := FMaxChars;
  774.     if HandleAllocated and (GetFocus = Handle) and
  775.        not (csDesigning in ComponentState) then
  776.       SetCursor(SelStart);
  777.   end;
  778. end;
  779.  
  780. function TCustomMaskEdit.GetMaxLength: Integer;
  781. begin
  782.   Result := inherited MaxLength;
  783. end;
  784.  
  785. procedure TCustomMaskEdit.SetMaxLength(Value: Integer);
  786. begin
  787.   if not IsMasked then
  788.     inherited MaxLength := Value
  789.   else
  790.     inherited MaxLength := FMaxChars;
  791. end;
  792.  
  793. procedure TCustomMaskEdit.GetSel(var SelStart: Integer; var SelStop: Integer);
  794. begin
  795.   SendMessage(Handle, EM_GETSEL, Integer(@SelStart), Integer(@SelStop));
  796. end;
  797.  
  798. procedure TCustomMaskEdit.SetSel(SelStart: Integer; SelStop: Integer);
  799. begin
  800.   SendMessage(Handle, EM_SETSEL, SelStart, SelStop);
  801. end;
  802.  
  803. procedure TCustomMaskEdit.SetCursor(Pos: Integer);
  804. var
  805.   SelStart, SelStop: Integer;
  806.   KeyState: TKeyboardState;
  807.   NewKeyState: TKeyboardState;
  808.   I: Integer;
  809. begin
  810.   if ByteType(EditText, Pos+1) = mbTrailByte then Dec(Pos);
  811.   SelStart := Pos;
  812.   if (IsMasked) then
  813.   begin
  814.     if SelStart < 0 then
  815.       SelStart := 0;
  816.     SelStop  := SelStart + 1;
  817.     if (Length(EditText) > SelStop) and (EditText[SelStop] in LeadBytes) then
  818.       Inc(SelStop);
  819.     if SelStart >= FMaxChars then
  820.     begin
  821.       SelStart := FMaxChars;
  822.       SelStop  := SelStart;
  823.     end;
  824.  
  825.     SetSel(SelStop, SelStop);
  826.  
  827.     if SelStart <> SelStop then
  828.     begin
  829.       GetKeyboardState(KeyState);
  830.       for I := Low(NewKeyState) to High(NewKeyState) do
  831.         NewKeyState[I] := 0;
  832.       NewKeyState [VK_SHIFT] := $81;
  833.       NewKeyState [VK_LEFT] := $81;
  834.       SetKeyboardState(NewKeyState);
  835.       SendMessage(Handle, WM_KEYDOWN, VK_LEFT, 1);
  836.       SendMessage(Handle, WM_KEYUP, VK_LEFT, 1);
  837.       SetKeyboardState(KeyState);
  838.     end;
  839.     FCaretPos := SelStart;
  840.   end
  841.   else
  842.   begin
  843.     if SelStart < 0 then
  844.       SelStart := 0;
  845.     if SelStart >= Length(EditText) then
  846.       SelStart := Length(EditText);
  847.     SetSel(SelStart, SelStart);
  848.   end;
  849. end;
  850.  
  851. procedure TCustomMaskEdit.CheckCursor;
  852. var
  853.   SelStart, SelStop: Integer;
  854. begin
  855.   if not HandleAllocated then  Exit;
  856.   if (IsMasked) then
  857.   begin
  858.     GetSel(SelStart, SelStop);
  859.     if SelStart = SelStop then
  860.       SetCursor(SelStart);
  861.   end;
  862. end;
  863.  
  864. procedure TCustomMaskEdit.Clear;
  865. begin
  866.   Text := '';
  867. end;
  868.  
  869. function TCustomMaskEdit.EditCanModify: Boolean;
  870. begin
  871.   Result := True;
  872. end;
  873.  
  874. procedure TCustomMaskEdit.Reset;
  875. begin
  876.   if Modified then
  877.   begin
  878.     EditText := FOldValue;
  879.     Modified := False;
  880.   end;
  881. end;
  882.  
  883. function TCustomMaskEdit.CharKeys(var CharCode: Char): Boolean;
  884. var
  885.   SelStart, SelStop : Integer;
  886.   Txt: string;
  887.   CharMsg: TMsg;
  888. begin
  889.   Result := False;
  890.   if Word(CharCode) = VK_ESCAPE then
  891.   begin
  892.     Reset;
  893.     Exit;
  894.   end;
  895.   if not EditCanModify or ReadOnly then Exit;
  896.   if (Word(CharCode) = VK_BACK) then Exit;
  897.   if (Word(CharCode) = VK_RETURN) then
  898.   begin
  899.     ValidateEdit;
  900.     Exit;
  901.   end;
  902.  
  903.   GetSel(SelStart, SelStop);
  904.   if (SelStop - SelStart) > 1 then
  905.   begin
  906.     DeleteKeys(VK_DELETE);
  907.     SelStart := GetNextEditChar(SelStart);
  908.     SetCursor(SelStart);
  909.   end;
  910.  
  911.   if (CharCode in LeadBytes) then
  912.     PeekMessage(CharMsg, Handle, WM_CHAR, WM_CHAR, PM_REMOVE);
  913.   Result := InputChar(CharCode, SelStart);
  914.   if Result then
  915.   begin
  916.     if (CharCode in LeadBytes) then
  917.     begin
  918.       Txt := CharCode + Char(CharMsg.wParam);
  919.       SetSel(SelStart, SelStart + 2);
  920.     end
  921.     else
  922.       Txt := CharCode;
  923.     SendMessage(Handle, EM_REPLACESEL, 0, LongInt(PChar(Txt)));
  924.     GetSel(SelStart, SelStop);
  925.     CursorInc(SelStart, 0);
  926.   end;
  927. end;
  928.  
  929. procedure TCustomMaskEdit.ArrowKeys(CharCode: Word; Shift: TShiftState);
  930. var
  931.   SelStart, SelStop : Integer;
  932. begin
  933.   if (ssCtrl in Shift) then Exit;
  934.   GetSel(SelStart, SelStop);
  935.   if (ssShift in Shift) then
  936.   begin
  937.     if (CharCode = VK_RIGHT) then
  938.     begin
  939.       Inc(FCaretPos);
  940.       if (SelStop = SelStart + 1) then
  941.       begin
  942.         SetSel(SelStart, SelStop);  {reset caret to end of string}
  943.         Inc(FCaretPos);
  944.       end;
  945.       if FCaretPos > FMaxChars then FCaretPos := FMaxChars;
  946.     end
  947.     else  {if (CharCode = VK_LEFT) then}
  948.     begin
  949.       Dec(FCaretPos);
  950.       if (SelStop = SelStart + 2) and
  951.         (FCaretPos > SelStart) then
  952.       begin
  953.         SetSel(SelStart + 1, SelStart + 1);  {reset caret to show up at start}
  954.         Dec(FCaretPos);
  955.       end;
  956.       if FCaretPos < 0 then FCaretPos := 0;
  957.     end;
  958.   end
  959.   else
  960.   begin
  961.     if (SelStop - SelStart) > 1 then
  962.     begin
  963.       if ((SelStop - SelStart) = 2) and (EditText[SelStart+1] in LeadBytes) then
  964.       begin
  965.         if (CharCode = VK_LEFT) then
  966.           CursorDec(SelStart)
  967.         else
  968.           CursorInc(SelStart, 2);
  969.         Exit;
  970.       end;
  971.       if SelStop = FCaretPos then
  972.         Dec(FCaretPos);
  973.       SetCursor(FCaretPos);
  974.     end
  975.     else if (CharCode = VK_LEFT) then
  976.       CursorDec(SelStart)
  977.     else   { if (CharCode = VK_RIGHT) then  }
  978.     begin
  979.       if SelStop = SelStart then
  980.         SetCursor(SelStart)
  981.       else
  982.         if EditText[SelStart+1] in LeadBytes then
  983.           CursorInc(SelStart, 2)
  984.         else
  985.           CursorInc(SelStart, 1);
  986.     end;
  987.   end;
  988. end;
  989.  
  990. procedure TCustomMaskEdit.CursorInc(CursorPos: Integer; Incr: Integer);
  991. var
  992.   NuPos: Integer;
  993. begin
  994.   NuPos := CursorPos + Incr;
  995.   NuPos := GetNextEditChar(NuPos);
  996.   if IsLiteralChar(EditMask, nuPos) then
  997.     NuPos := CursorPos;
  998.   SetCursor(NuPos);
  999. end;
  1000.  
  1001.  
  1002. procedure TCustomMaskEdit.CursorDec(CursorPos: Integer);
  1003. var
  1004.   nuPos: Integer;
  1005. begin
  1006.   nuPos := CursorPos;
  1007.   Dec(nuPos);
  1008.   nuPos := GetPriorEditChar(nuPos);
  1009.   SetCursor(NuPos);
  1010. end;
  1011.  
  1012. function TCustomMaskEdit.GetFirstEditChar: Integer;
  1013. begin
  1014.   Result := 0;
  1015.   if IsMasked then
  1016.     Result := GetNextEditChar(0);
  1017. end;
  1018.  
  1019. function TCustomMaskEdit.GetLastEditChar: Integer;
  1020. begin
  1021.   Result := GetMaxChars;
  1022.   if IsMasked then
  1023.     Result := GetPriorEditChar(Result - 1);
  1024. end;
  1025.  
  1026. function TCustomMaskEdit.GetNextEditChar(Offset: Integer): Integer;
  1027. begin
  1028.   Result := Offset;
  1029.   while(Result < FMaxChars) and (IsLiteralChar(EditMask, Result)) do
  1030.     Inc(Result);
  1031. end;
  1032.  
  1033. function TCustomMaskEdit.GetPriorEditChar(Offset: Integer): Integer;
  1034. begin
  1035.   Result := Offset;
  1036.   while(Result >= 0) and (IsLiteralChar(EditMask, Result)) do
  1037.     Dec(Result);
  1038.   if Result < 0 then
  1039.     Result := GetNextEditChar(Result);
  1040. end;
  1041.  
  1042. procedure TCustomMaskEdit.HomeEndKeys(CharCode: Word; Shift: TShiftState);
  1043. var
  1044.   SelStart, SelStop : Integer;
  1045. begin
  1046.   GetSel(SelStart, SelStop);
  1047.   if (CharCode = VK_HOME) then
  1048.   begin
  1049.     if (ssShift in Shift) then
  1050.     begin
  1051.       if (SelStart <> FCaretPos) and (SelStop <> (SelStart + 1)) then
  1052.         SelStop := SelStart + 1;
  1053.       SetSel(0, SelStop);
  1054.       CheckCursor;
  1055.     end
  1056.     else
  1057.       SetCursor(0);
  1058.     FCaretPos := 0;
  1059.   end
  1060.   else
  1061.   begin
  1062.     if (ssShift in Shift) then
  1063.     begin
  1064.       if (SelStop <> FCaretPos) and (SelStop <> (SelStart + 1)) then
  1065.         SelStart := SelStop - 1;
  1066.       SetSel(SelStart, FMaxChars);
  1067.       CheckCursor;
  1068.     end
  1069.     else
  1070.       SetCursor(FMaxChars);
  1071.     FCaretPos := FMaxChars;
  1072.   end;
  1073. end;
  1074.  
  1075. procedure TCustomMaskEdit.DeleteKeys(CharCode: Word);
  1076. var
  1077.   SelStart, SelStop : Integer;
  1078.   NuSelStart: Integer;
  1079.   Str: string;
  1080. begin
  1081.   if ReadOnly then Exit;
  1082.   GetSel(SelStart, SelStop);
  1083.   if ((SelStop - SelStart) <= 1) and (CharCode = VK_BACK) then
  1084.   begin
  1085.     NuSelStart := SelStart;
  1086.     CursorDec(SelStart);
  1087.     GetSel(SelStart, SelStop);
  1088.     if SelStart = NuSelStart then Exit;
  1089.   end;
  1090.  
  1091.   if (SelStop - SelStart) < 1 then Exit;
  1092.  
  1093.   Str := EditText;
  1094.   DeleteSelection(Str, SelStart, SelStop - SelStart);
  1095.   Str := Copy(Str, SelStart+1, SelStop - SelStart);
  1096.   SendMessage(Handle, EM_REPLACESEL, 0, LongInt(PChar(Str)));
  1097.   if (SelStop - SelStart) <> 1 then
  1098.   begin
  1099.     SelStart := GetNextEditChar(SelStart);
  1100.     SetCursor(SelStart);
  1101.   end
  1102.   else begin
  1103.     GetSel(SelStart, SelStop);
  1104.     SetCursor(SelStart - 1);
  1105.   end;
  1106. end;
  1107.  
  1108. procedure TCustomMaskEdit.CMEnter(var Message: TCMEnter);
  1109. begin
  1110.   if IsMasked and not (csDesigning in ComponentState) then
  1111.   begin
  1112.     if not (msReEnter in FMaskState) then
  1113.     begin
  1114.       FOldValue := EditText;
  1115.       inherited;
  1116.     end;
  1117.     Exclude(FMaskState, msReEnter);
  1118.     CheckCursor;
  1119.   end
  1120.   else
  1121.     inherited;
  1122. end;
  1123.  
  1124. procedure TCustomMaskEdit.CMTextChanged(var Message: TMessage);
  1125. var
  1126.   SelStart, SelStop : Integer;
  1127.   Temp: Integer;
  1128. begin
  1129.   inherited;
  1130.   FOldValue := EditText;
  1131.   if HandleAllocated then
  1132.   begin
  1133.     GetSel(SelStart, SelStop);
  1134.     Temp := GetNextEditChar(SelStart);
  1135.     if Temp <> SelStart then
  1136.       SetCursor(Temp);
  1137.   end;
  1138. end;
  1139.  
  1140. procedure TCustomMaskEdit.CMWantSpecialKey(var Message: TCMWantSpecialKey);
  1141. begin
  1142.   inherited;
  1143.   if (Message.CharCode = VK_ESCAPE) and IsMasked and Modified then
  1144.     Message.Result := 1;
  1145. end;
  1146.  
  1147. procedure TCustomMaskEdit.CMExit(var Message: TCMExit);
  1148. begin
  1149.   if IsMasked and not (csDesigning in ComponentState) then
  1150.   begin
  1151.     ValidateEdit;
  1152.     CheckCursor;
  1153.   end;
  1154.   inherited;
  1155. end;
  1156.  
  1157. procedure TCustomMaskEdit.ValidateEdit;
  1158. var
  1159.   Str: string;
  1160.   Pos: Integer;
  1161. begin
  1162.   Str := EditText;
  1163.   if IsMasked and Modified then
  1164.   begin
  1165.     if not Validate(Str, Pos) then
  1166.     begin
  1167.       if not (csDesigning in ComponentState) then
  1168.       begin
  1169.         Include(FMaskState, msReEnter);
  1170.         SetFocus;
  1171.       end;
  1172.       SetCursor(Pos);
  1173.       ValidateError;
  1174.     end;
  1175.   end;
  1176. end;
  1177.  
  1178. procedure TCustomMaskEdit.ValidateError;
  1179. var
  1180.   Str: string;
  1181. begin
  1182.   MessageBeep(0);
  1183.   Str := EditMask;
  1184.   Str := Format(SMaskEditErr, [Str]);
  1185.   raise EDBEditError.Create(Str);
  1186. end;
  1187.  
  1188. function TCustomMaskEdit.AddEditFormat(const Value: string; Active: Boolean): string;
  1189. begin
  1190.   if not Active then
  1191.     Result := MaskDoFormatText(EditMask, Value, ' ')
  1192.   else
  1193.     Result := MaskDoFormatText(EditMask, Value, FMaskBlank);
  1194. end;
  1195.  
  1196. function TCustomMaskEdit.RemoveEditFormat(const Value: string): string;
  1197. var
  1198.   I: Integer;
  1199.   OldLen: Integer;
  1200.   Offset, MaskOffset: Integer;
  1201.   CType: TMaskCharType;
  1202.   Dir: TMaskDirectives;
  1203. begin
  1204.   Offset := 1;
  1205.   Result := Value;
  1206.   for MaskOffset := 1 to Length(EditMask) do
  1207.   begin
  1208.     CType := MaskGetCharType(EditMask, MaskOffset);
  1209.  
  1210.     if CType in [mcLiteral, mcIntlLiteral] then
  1211.       Result := Copy(Result, 1, Offset - 1) +
  1212.         Copy(Result, Offset + 1, Length(Result) - Offset);
  1213.     if CType in [mcMask, mcMaskOpt] then Inc(Offset);
  1214.   end;
  1215.  
  1216.   Dir := MaskGetCurrentDirectives(EditMask, 1);
  1217.   if mdReverseDir in Dir then
  1218.   begin
  1219.     Offset := 1;
  1220.     for I := 1 to Length(Result) do
  1221.     begin
  1222.       if Result[I] = FMaskBlank then
  1223.         Inc(Offset)
  1224.       else
  1225.         break;
  1226.     end;
  1227.     if Offset <> 1 then
  1228.       Result := Copy(Result, Offset, Length(Result) - Offset + 1);
  1229.   end
  1230.   else begin
  1231.     OldLen := Length(Result);
  1232.     for I := 1 to OldLen do
  1233.     begin
  1234.       if Result[OldLen - I + 1] = FMaskBlank then
  1235.         SetLength(Result, Length(Result) - 1)
  1236.       else Break;
  1237.     end;
  1238.   end;
  1239.   if FMaskBlank <> ' ' then
  1240.   begin
  1241.     OldLen := Length(Result);
  1242.     for I := 1 to OldLen do
  1243.     begin
  1244.       if Result[I] = FMaskBlank then
  1245.         Result[I] := ' ';
  1246.       if I > OldLen then Break;
  1247.     end;
  1248.   end;
  1249. end;
  1250.  
  1251. function TCustomMaskEdit.InputChar(var NewChar: Char; Offset: Integer): Boolean;
  1252. var
  1253.   MaskOffset: Integer;
  1254.   CType: TMaskCharType;
  1255.   InChar: Char;
  1256. begin
  1257.   Result := True;
  1258.   if EditMask <> '' then
  1259.   begin
  1260.     Result := False;
  1261.     MaskOffset := OffsetToMaskOffset(EditMask, Offset);
  1262.     if MaskOffset >= 0 then
  1263.     begin
  1264.       CType := MaskGetCharType(EditMask, MaskOffset);
  1265.       InChar := NewChar;
  1266.       Result := DoInputChar(NewChar, MaskOffset);
  1267.       if not Result and (CType in [mcMask, mcMaskOpt]) then
  1268.       begin
  1269.         MaskOffset := FindLiteralChar (MaskOffset, InChar);
  1270.         if MaskOffset > 0 then
  1271.         begin
  1272.           MaskOffset := MaskOffsetToOffset(EditMask, MaskOffset);
  1273.           SetCursor (MaskOffset);
  1274.           Exit;
  1275.         end;
  1276.       end;
  1277.     end;
  1278.   end;
  1279.   if not Result then
  1280.     MessageBeep(0)
  1281. end;
  1282.  
  1283. function TCustomMaskEdit.DoInputChar(var NewChar: Char; MaskOffset: Integer): Boolean;
  1284. var
  1285.   Dir: TMaskDirectives;
  1286.   Str: string;
  1287.   CType: TMaskCharType;
  1288.  
  1289.   function IsKatakana(const Chr: Byte): Boolean;
  1290.   begin
  1291.     Result := (SysLocale.PriLangID = LANG_JAPANESE) and (Chr in [$A1..$DF]);
  1292.   end;
  1293.  
  1294.   function TestChar(NewChar: Char): Boolean;
  1295.   var
  1296.     Offset: Integer;
  1297.   begin
  1298.     Offset := MaskOffsetToOffset(EditMask, MaskOffset);
  1299.     Result := not ((MaskOffset < Length(EditMask)) and
  1300.                (UpCase(EditMask[MaskOffset]) = UpCase(EditMask[MaskOffset+1]))) or
  1301.                (ByteType(EditText, Offset) = mbTrailByte) or
  1302.                (ByteType(EditText, Offset+1) = mbLeadByte);
  1303.   end;
  1304.  
  1305. begin
  1306.   Result := True;
  1307.   CType := MaskGetCharType(EditMask, MaskOffset);
  1308.   if CType in [mcLiteral, mcIntlLiteral] then
  1309.     NewChar := MaskIntlLiteralToChar(EditMask[MaskOffset])
  1310.   else
  1311.   begin
  1312.     Dir := MaskGetCurrentDirectives(EditMask, MaskOffset);
  1313.     case EditMask[MaskOffset] of
  1314.       mMskNumeric, mMskNumericOpt:
  1315.         begin
  1316.           if not ((NewChar >= '0') and (NewChar <= '9')) then
  1317.             Result := False;
  1318.         end;
  1319.       mMskNumSymOpt:
  1320.         begin
  1321.           if not (((NewChar >= '0') and (NewChar <= '9')) or
  1322.                  (NewChar = ' ') or(NewChar = '+') or(NewChar = '-')) then
  1323.             Result := False;
  1324.         end;
  1325.       mMskAscii, mMskAsciiOpt:
  1326.         begin
  1327.           if (NewChar in LeadBytes) and TestChar(NewChar) then
  1328.           begin
  1329.             Result := False;
  1330.             Exit;
  1331.           end;
  1332.           if IsCharAlpha(NewChar) then
  1333.           begin
  1334.             Str := ' ';
  1335.             Str[1] := NewChar;
  1336.             if (mdUpperCase in Dir)  then
  1337.               Str := AnsiUpperCase(Str)
  1338.             else if mdLowerCase in Dir then
  1339.               Str := AnsiLowerCase(Str);
  1340.             NewChar := Str[1];
  1341.           end;
  1342.         end;
  1343.       mMskAlpha, mMskAlphaOpt, mMskAlphaNum, mMskAlphaNumOpt:
  1344.         begin
  1345.           if (NewChar in LeadBytes) then
  1346.           begin
  1347.             if TestChar(NewChar) then
  1348.               Result := False;
  1349.             Exit;
  1350.           end;
  1351.           Str := ' ';
  1352.           Str[1] := NewChar;
  1353.           if not IsCharAlpha(NewChar) then
  1354.           begin
  1355.             Result := False;
  1356.             if ((EditMask[MaskOffset] = mMskAlphaNum) or
  1357.                 (EditMask[MaskOffset] = mMskAlphaNumOpt)) and
  1358.                 (IsCharAlphaNumeric(NewChar) or
  1359.                  IsKatakana(Byte(NewChar))) then
  1360.               Result := True;
  1361.           end
  1362.           else if mdUpperCase in Dir then
  1363.             Str := AnsiUpperCase(Str)
  1364.           else if mdLowerCase in Dir then
  1365.             Str := AnsiLowerCase(Str);
  1366.           NewChar := Str[1];
  1367.         end;
  1368.     end;
  1369.   end;
  1370. end;
  1371.  
  1372. function TCustomMaskEdit.Validate(const Value: string; var Pos: Integer): Boolean;
  1373. var
  1374.   Offset, MaskOffset: Integer;
  1375.   CType: TMaskCharType;
  1376. begin
  1377.   Result := True;
  1378.   Offset := 1;
  1379.   for MaskOffset := 1 to Length(EditMask) do
  1380.   begin
  1381.     CType := MaskGetCharType(EditMask, MaskOffset);
  1382.  
  1383.     if CType in [mcLiteral, mcIntlLiteral, mcMaskOpt] then
  1384.       Inc(Offset)
  1385.     else if (CType = mcMask) and (Value <> '') then
  1386.     begin
  1387.       if (Value [Offset] = FMaskBlank) or
  1388.         ((Value [Offset] = ' ') and (EditMask[MaskOffset] <> mMskAscii)) then
  1389.       begin
  1390.         Result := False;
  1391.         Pos := Offset - 1;
  1392.         Exit;
  1393.       end;
  1394.       Inc(Offset);
  1395.     end;
  1396.   end;
  1397. end;
  1398.  
  1399. function TCustomMaskEdit.DeleteSelection(var Value: string; Offset: Integer;
  1400.   Len: Integer): Boolean;
  1401. var
  1402.   EndDel: Integer;
  1403.   StrOffset, MaskOffset, Temp: Integer;
  1404.   CType: TMaskCharType;
  1405. begin
  1406.   Result := True;
  1407.   if Len = 0 then Exit;
  1408.  
  1409.   StrOffset := Offset + 1;
  1410.   EndDel := StrOffset + Len;
  1411.   Temp := OffsetToMaskOffset(EditMask, Offset);
  1412.   if Temp < 0 then  Exit;
  1413.   for MaskOffset := Temp to Length(EditMask) do
  1414.   begin
  1415.     CType := MaskGetCharType(EditMask, MaskOffset);
  1416.     if CType in [mcLiteral, mcIntlLiteral] then
  1417.       Inc(StrOffset)
  1418.     else if CType in [mcMask, mcMaskOpt] then
  1419.     begin
  1420.       Value[StrOffset] := FMaskBlank;
  1421.       Inc(StrOffset);
  1422.     end;
  1423.     if StrOffset >= EndDel then Break;
  1424.   end;
  1425. end;
  1426.  
  1427. function TCustomMaskEdit.InputString(var Value: string; const NewValue: string;
  1428.   Offset: Integer): Integer;
  1429. var
  1430.   NewOffset, MaskOffset, Temp: Integer;
  1431.   CType: TMaskCharType;
  1432.   NewVal: string;
  1433.   NewChar: Char;
  1434. begin
  1435.   Result := Offset;
  1436.   if NewValue = '' then Exit;
  1437.   { replace chars with new chars, except literals }
  1438.   NewOffset := 1;
  1439.   NewVal := NewValue;
  1440.   Temp := OffsetToMaskOffset(EditMask, Offset);
  1441.   if Temp < 0 then  Exit;
  1442.   MaskOffset := Temp;
  1443.   While MaskOffset <= Length(EditMask) do
  1444.   begin
  1445.     CType := MaskGetCharType(EditMask, MaskOffset);
  1446.     if CType in [mcLiteral, mcIntlLiteral, mcMask, mcMaskOpt] then
  1447.     begin
  1448.       NewChar := NewVal[NewOffset];
  1449.       if not (DoInputChar(NewChar, MaskOffset)) then
  1450.       begin
  1451.         if (NewChar in LeadBytes) then
  1452.           NewVal[NewOffset + 1] := FMaskBlank;
  1453.         NewChar := FMaskBlank;
  1454.       end;
  1455.         { if pasted text does not contain a literal in the right place,
  1456.           insert one }
  1457.       if not ((CType in [mcLiteral, mcIntlLiteral]) and
  1458.         (NewChar <> NewVal[NewOffset])) then
  1459.       begin
  1460.         NewVal[NewOffset] := NewChar;
  1461.         if (NewChar in LeadBytes) then
  1462.         begin
  1463.           Inc(NewOffset);
  1464.           Inc(MaskOffset);
  1465.         end;
  1466.       end
  1467.       else
  1468.         NewVal := Copy(NewVal, 1, NewOffset-1) + NewChar +
  1469.           Copy(NewVal, NewOffset, Length (NewVal));
  1470.       Inc(NewOffset);
  1471.     end;
  1472.     if (NewOffset + Offset) > FMaxChars then Break;
  1473.     if (NewOffset) > Length(NewVal) then Break;
  1474.     Inc(MaskOffset);
  1475.   end;
  1476.  
  1477.   if (Offset + Length(NewVal)) < FMaxChars then
  1478.   begin
  1479.     if ByteType(Value, OffSet + Length(NewVal) + 1) = mbTrailByte then
  1480.     begin
  1481.       NewVal := NewVal + FMaskBlank;
  1482.       Inc(NewOffset);
  1483.     end;
  1484.     Value := Copy(Value, 1, Offset) + NewVal +
  1485.       Copy(Value, OffSet + Length(NewVal) + 1,
  1486.         FMaxChars -(Offset + Length(NewVal)));
  1487.   end
  1488.   else
  1489.   begin
  1490.     Temp := Offset;
  1491.     if (ByteType(NewVal, FMaxChars - Offset) = mbLeadByte) then
  1492.       Inc(Temp);
  1493.     Value := Copy(Value, 1, Offset) +
  1494.              Copy(NewVal, 1, FMaxChars - Temp);
  1495.   end;
  1496.   Result := NewOffset + Offset - 1;
  1497. end;
  1498.  
  1499. function TCustomMaskEdit.FindLiteralChar(MaskOffset: Integer; InChar: Char): Integer;
  1500. var
  1501.   CType: TMaskCharType;
  1502.   LitChar: Char;
  1503. begin
  1504.   Result := -1;
  1505.   while MaskOffset < Length(EditMask) do
  1506.   begin
  1507.     Inc(MaskOffset);
  1508.     CType := MaskGetCharType(EditMask, MaskOffset);
  1509.     if CType in [mcLiteral, mcIntlLiteral] then
  1510.     begin
  1511.       LitChar := EditMask[MaskOffset];
  1512.       if CType = mcIntlLiteral then
  1513.         LitChar := MaskIntlLiteralToChar(LitChar);
  1514.       if LitChar = InChar then
  1515.         Result := MaskOffset;
  1516.       Exit;
  1517.     end;
  1518.   end;
  1519. end;
  1520.  
  1521. end.
  1522.